﻿#include "EditorScriptDebugSubsystem.h"

#include "Path/Path.h"

#include "Project/ProjectModel.h"
#include "File/JsonUtils.h"
#include "Network/TCPClient.h"
#include "Network/TCPServer.h"

#include "Framework/Editor.h"
#include "Async/Process.h"

#include <thread>
#include <string.h>


void REditorScriptDebugSubsystem::AddBreakpoint(const CString &InFileId, int64_t InLineIndex)
{
   auto Lines=BreakpointMap.Find(InFileId);
   if(Lines)
   {
        auto Breakpoint=Lines->Find(InLineIndex);
        if(Breakpoint)
        {
            (*Breakpoint)->bEnabled=true;
        }
        else
        {
            auto NewBreakpoint=NewObject<REditorBreakpoint>(this);
            NewBreakpoint->FileId=InFileId;
            NewBreakpoint->LineIndex=InLineIndex;
            NewBreakpoint->bEnabled=true;
            BreakpointList.Add(NewBreakpoint);

            Lines->Add(InLineIndex,NewBreakpoint);

            PostBreakpointAdded(InFileId,InLineIndex);

        }
   }
   else
   {
        auto NewBreakpoint=NewObject<REditorBreakpoint>(this);
        NewBreakpoint->FileId=InFileId;
        NewBreakpoint->LineIndex=InLineIndex;
        NewBreakpoint->bEnabled=true;
        BreakpointList.Add(NewBreakpoint);

        TMap<int64_t,TSharedObjectPtr<REditorBreakpoint>> Lines;
        Lines.Add(InLineIndex,NewBreakpoint);

        BreakpointMap.Add(InFileId,Lines);

        PostBreakpointAdded(InFileId,InLineIndex);
    
   }
}

void REditorScriptDebugSubsystem::RemoveBreakpoint(const CString &InFileId, int64_t InLineIndex)
{
    auto Lines=BreakpointMap.Find(InFileId);
    if(Lines)
    {
        auto Breakpoint=Lines->Find(InLineIndex);
        if(Breakpoint)
        {
            Lines->Remove(InLineIndex);
            if(Lines->Num()==0)
            {
                BreakpointMap.Remove(InFileId);
            }

            BreakpointList.Remove(*Breakpoint);

            PostBreakpointRemoved(InFileId,InLineIndex);

        }
    }
}

void REditorScriptDebugSubsystem::AddOrRemoveBreakpoint(const CString &InFileId, int64_t InLineIndex)
{
    auto Lines=BreakpointMap.Find(InFileId);
    if(Lines)
    {
        auto Breakpoint=Lines->Find(InLineIndex);
        if(Breakpoint)
        {
            BreakpointList.Remove(*Breakpoint);
            Lines->Remove(InLineIndex);
            if(Lines->Num()==0)
            {
                BreakpointMap.Remove(InFileId);
            }

            PostBreakpointRemoved(InFileId,InLineIndex);
        }
        else
        {
            auto NewBreakpoint=NewObject<REditorBreakpoint>(this);
            NewBreakpoint->FileId=InFileId;
            NewBreakpoint->LineIndex=InLineIndex;
            NewBreakpoint->bEnabled=true;
            BreakpointList.Add(NewBreakpoint);

            Lines->Add(InLineIndex,NewBreakpoint);

            PostBreakpointAdded(InFileId,InLineIndex);
        }
    }
    else
    {
        auto NewBreakpoint=NewObject<REditorBreakpoint>(this);
        NewBreakpoint->FileId=InFileId;
        NewBreakpoint->LineIndex=InLineIndex;
        NewBreakpoint->bEnabled=true;
        BreakpointList.Add(NewBreakpoint);

        TMap<int64_t,TSharedObjectPtr<REditorBreakpoint>> Lines;
        Lines.Add(InLineIndex,NewBreakpoint);

        BreakpointMap.Add(InFileId,Lines);

        PostBreakpointAdded(InFileId,InLineIndex);
    }
}

void REditorScriptDebugSubsystem::DisableBreakpoint(const CString &InFileId, int64_t InLineIndex)
{
   auto Lines=BreakpointMap.Find(InFileId);
   if(Lines)
   {
        auto Breakpoint=Lines->Find(InLineIndex);
        if(Breakpoint)
        {
            (*Breakpoint)->bEnabled=false;

            PostBreakpointStateChanged(InFileId,InLineIndex);
        }
   }

}

void REditorScriptDebugSubsystem::EnableBreakpoint(const CString &InFileId, int64_t InLineIndex)
{
   auto Lines=BreakpointMap.Find(InFileId);
   if(Lines)
   {
        auto Breakpoint=Lines->Find(InLineIndex);
        if(Breakpoint)
        {
            (*Breakpoint)->bEnabled=true;

            PostBreakpointStateChanged(InFileId,InLineIndex);
        }
   }
}

void REditorScriptDebugSubsystem::ToggleBreakpoint(const CString &InFileId, int64_t InLineIndex)
{
    auto Lines=BreakpointMap.Find(InFileId);
    if(Lines)
    {
          auto Breakpoint=Lines->Find(InLineIndex);
          if(Breakpoint)
          {
                (*Breakpoint)->bEnabled=!(*Breakpoint)->bEnabled;

                PostBreakpointStateChanged(InFileId,InLineIndex);
          }
    }
}

TOptional<bool> REditorScriptDebugSubsystem::GetBreakpointType(const CString &InFileId, int64_t InLineIndex) const
{
    auto Lines=BreakpointMap.Find(InFileId);
    if(Lines)
    {
        auto Breakpoint=Lines->Find(InLineIndex);
        if(Breakpoint)
        {
            return (*Breakpoint)->bEnabled;
        }
    }
    return TOptional<bool>();
}

int64_t REditorScriptDebugSubsystem::GetBreakpointNumber() const
{
    return BreakpointList.Num();
}

bool REditorScriptDebugSubsystem::IsBreakpointEnabled(int64_t InBreakpointIndex) const
{
    return BreakpointList[InBreakpointIndex]->bEnabled;
}

void REditorScriptDebugSubsystem::TogglerBreakpointEnabled(int64_t InBreakpointIndex)
{
    BreakpointList[InBreakpointIndex]->bEnabled=!BreakpointList[InBreakpointIndex]->bEnabled;

    PostBreakpointStateChanged(BreakpointList[InBreakpointIndex]->FileId,BreakpointList[InBreakpointIndex]->LineIndex);
}

CString REditorScriptDebugSubsystem::GetBreakpointFileName(int64_t InBreakpointIndex) const
{
    SPath Path(BreakpointList[InBreakpointIndex]->FileId);
    return Path.GetFileOrDirectoryName();
}

CString REditorScriptDebugSubsystem::GetBreakpointFilePath(int64_t InBreakpointIndex) const
{
    return BreakpointList[InBreakpointIndex]->FileId;
}

int64_t REditorScriptDebugSubsystem::GetBreakpointLine(int64_t InBreakpointIndex) const
{
    return BreakpointList[InBreakpointIndex]->LineIndex;
}

void REditorScriptDebugSubsystem::RemoveBreakpoint(int64_t InBreakpointIndex)
{
    auto Breakpoint=BreakpointList[InBreakpointIndex];
    RemoveBreakpoint(Breakpoint->FileId,Breakpoint->LineIndex);
}

TVector<CString> REditorScriptDebugSubsystem::GetBreakpointFileList() const
{
    return BreakpointMap.Keys();
}

TVector<TSharedObjectPtr<REditorBreakpoint>> REditorScriptDebugSubsystem::GetBreakpointLineList(const CString &InFileId) const
{
    auto Lines=BreakpointMap.Find(InFileId);
    if(Lines)
    {
        return Lines->Values();
    }
    return TVector<TSharedObjectPtr<REditorBreakpoint>>();
}

void REditorScriptDebugSubsystem::SetBreakpoints(CString InFileId, const TVector<TTuple<int64_t, bool>>& InBreakpointList)
{
    auto Lines=BreakpointMap.Find(InFileId);
    if(Lines)
    {
        //remove the breakpoint which is not in the list
        auto AllBreakpointLines=Lines->Keys();
        for(auto & Line:AllBreakpointLines)
        {
            auto Index=InBreakpointList.FindBy([Line](const TTuple<int64_t,bool> & InBreakpoint)
            {
                return InBreakpoint.First==Line;
            });

            if(Index.HasValue())
            {
                continue;
            }

            auto Breakpoint=Lines->Find(Line);
            assert(Breakpoint);
            BreakpointList.Remove(*Breakpoint);
            Lines->Remove(Line);

            PostBreakpointRemoved(InFileId,Line);

        }

        for(auto & Breakpoint:InBreakpointList)
        {
            auto BreakpointPtr=Lines->Find(Breakpoint.First);
            if(BreakpointPtr)
            {
                (*BreakpointPtr)->bEnabled=Breakpoint.Second;
            }
            else
            {
                auto NewBreakpoint=NewObject<REditorBreakpoint>(this);
                NewBreakpoint->FileId=InFileId;
                NewBreakpoint->LineIndex=Breakpoint.First;
                NewBreakpoint->bEnabled=Breakpoint.Second;
                BreakpointList.Add(NewBreakpoint);

                Lines->Add(Breakpoint.First,NewBreakpoint);

                PostBreakpointAdded(InFileId,Breakpoint.First);
            }
        }


        //remove the empty file
        if(Lines->Empty())
        {
            BreakpointMap.Remove(InFileId);
        }

    }
    else
    {
        TMap<int64_t,TSharedObjectPtr<REditorBreakpoint>> Lines;
        for(auto & Breakpoint:InBreakpointList)
        {
            auto NewBreakpoint=NewObject<REditorBreakpoint>(this);
            NewBreakpoint->FileId=InFileId;
            NewBreakpoint->LineIndex=Breakpoint.First;
            NewBreakpoint->bEnabled=Breakpoint.Second;
            BreakpointList.Add(NewBreakpoint);

            Lines.Add(Breakpoint.First,NewBreakpoint);

            PostBreakpointAdded(InFileId,Breakpoint.First);
        }
        BreakpointMap.Add(InFileId,Lines);
    }

}




void REditorScriptDebugSubsystem::SetCallStack(const TVector<SEditorCallStackFrame> &InCallStack)
{
    CallStack = InCallStack;

    SelectedFrame=-1;

    OnCallStackChanged.Broadcast();
}

void REditorScriptDebugSubsystem::SelectFrame(int64_t InFrameIndex)
{
    SelectedFrame=InFrameIndex;

    RequestLocalVariable();
}

int64_t REditorScriptDebugSubsystem::GetSelectedFrame() const
{
    return SelectedFrame;
}

int64_t REditorScriptDebugSubsystem::GetCallStackFrameNum() const
{
    return CallStack.Num();
}

CString REditorScriptDebugSubsystem::GetCallStackFrameFunctionName(int64_t InIndex) const
{
    return CallStack[InIndex].FunctionName;
}

CString REditorScriptDebugSubsystem::GetCallStackFrameFileName(int64_t InIndex) const
{
    return CallStack[InIndex].FileName;
}

int64_t REditorScriptDebugSubsystem::GetCallStackFrameLineNumber(int64_t InIndex) const
{
    return CallStack[InIndex].LineNumber;
}

void REditorScriptDebugSubsystem::SetSelectedFrameData(const SEditorScriptFrameData &InFrameData)
{
    SelectedFrameData = InFrameData;
    OnSelectedFrameDataChanged.Broadcast();
}

int64_t REditorScriptDebugSubsystem::GetSelectedFrameDataParameterNum() const
{
    return SelectedFrameData.Parameters.Num();
}

CString REditorScriptDebugSubsystem::GetSelectedFrameDataParameterName(int64_t InIndex) const
{
    return SelectedFrameData.Parameters[InIndex].Name;
}

CString REditorScriptDebugSubsystem::GetSelectedFrameDataParameterValue(int64_t InIndex) const
{
    return SelectedFrameData.Parameters[InIndex].Value;
}

SEditorScriptValueReference REditorScriptDebugSubsystem::GetSelectedFrameDataParameterReference(int64_t InIndex) const
{
    return SelectedFrameData.Parameters[InIndex].Reference;
}

bool REditorScriptDebugSubsystem::HasSelectedFrameDataParameterChildren(int64_t InIndex) const
{
    return SelectedFrameData.Parameters[InIndex].HasChildren();
}

int64_t REditorScriptDebugSubsystem::GetSelectedFrameDataLocalVariableNum() const
{
    return SelectedFrameData.LocalVariables.Num();
}

CString REditorScriptDebugSubsystem::GetSelectedFrameDataLocalVariableName(int64_t InIndex) const
{
    return SelectedFrameData.LocalVariables[InIndex].Name;
}

CString REditorScriptDebugSubsystem::GetSelectedFrameDataLocalVariableValue(int64_t InIndex) const
{
    return SelectedFrameData.LocalVariables[InIndex].Value;
}

SEditorScriptValueReference REditorScriptDebugSubsystem::GetSelectedFrameDataLocalVariableReference(int64_t InIndex) const
{
    return SelectedFrameData.LocalVariables[InIndex].Reference;
}

bool REditorScriptDebugSubsystem::HasSelectedFrameDataLocalVariableChildren(int64_t InIndex) const
{
    return SelectedFrameData.LocalVariables[InIndex].HasChildren();
}

void REditorScriptDebugSubsystem::RequestReferenceChildren(const SEditorScriptValueReference &InReference)
{
    auto RequestJson=std::make_shared<CJsonObjectNode>();
    {
        auto ReferenceJson=std::make_shared<CJsonObjectNode>();
        ReferenceJson->Add(U"TypeName",std::make_shared<CJsonStringNode>(InReference.TypeName));
        ReferenceJson->Add(U"Address",std::make_shared<CJsonStringNode>(InReference.Address));
        RequestJson->Add(U"Reference",ReferenceJson);
    }
    
    SendToServer(U"ReferenceChildren:Editor"
        + CJsonUtils::ToString(RequestJson)
    );
}

void REditorScriptDebugSubsystem::Launch(uint16_t Port)
{
    RProjectModelPtr ProjectManager=REditor::Get()->GetSubsystem<RProjectModel>();

    //1.start the server
    if(ClientState!=EDebugClientState::NotRunning)
    {
        WH::PrintLine(U"已经在调试状态了，无法开启新的调试");
        return;
    }

    //检查端口是否被占用
    if(CTCPServer::IsPortOccupied(SIPAddress::LocalHostIPv4,Port))
    {
        WH::PrintLine(U"无法开启调试，端口被占用");
        return;
    }


    //2.launch the project
    CString LaunchCommand=ProjectManager->CreateLaunchCommand(true,Port);
    if(!ProjectProcess)
    {
        ProjectProcess=NewObject<RProcess>(this);
        //ProjectProcess->OnFinished.Bind(this,&REditorScriptDebugSubsystem::OnProjectExited);
        ProjectProcess->OnStandardError.Bind(this,&REditorScriptDebugSubsystem::OnProjectError);
    }

    ProjectProcess->Start(LaunchCommand,TVector<CString>(),false,true);
    
    if(ProjectProcess->GetState()!=EProcessState::Running)
    {
        WH::PrintLine(U"启动项目失败! 有可能是缺少动态库");
        ClientState=EDebugClientState::NotRunning;
        return;
    }
    //3.wait for server to connect
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));

    //4.connect
    ConnectToServer(Port);
    
}

void REditorScriptDebugSubsystem::AttachTo(uint16_t Port)
{
    if(ClientState!=EDebugClientState::NotRunning)
    {
        WH::PrintLine(U"已经在调试状态了，无法开启新的调试");
        return;
    }

    ConnectToServer(Port);
    return;    
}

void REditorScriptDebugSubsystem::Pause()
{
    if(ClientState==EDebugClientState::Connected)
    {
        WH::PrintLine(U"当前不提供暂停程序的功能");
    }
}

void REditorScriptDebugSubsystem::Continue()
{
    if(ClientState==EDebugClientState::Paused)
    {
        SendToServer(U"Continue:Editor");
    }
}

void REditorScriptDebugSubsystem::Stop()
{
    if(ClientState==EDebugClientState::NotRunning
       // || ClientState==EDebugClientState::Disconnecting
        )
    {
        return;
    }

    if(ProjectProcess && ProjectProcess->GetState()==EProcessState::Running)
    {
        ProjectProcess->Stop();
    }
    else
    {
        //是采用附加到进程启动的，所以不需要关闭进程
        //SendToServer(U"Detach:Editor");
        Client->Disconnect();
    }
    
    
    PostProjectExited();
}

void REditorScriptDebugSubsystem::SyncAllBreakpoints()
{
    auto AllFiles=GetBreakpointFileList();

    for(auto &FileID:AllFiles)
    {
        SyncBreakpoints(FileID);
    }

}

void REditorScriptDebugSubsystem::SyncBreakpoints(const CString &InFileID)
{
    if(bDonotSyncBreakpoints)
    {
        return;
    }

    if(ClientState!=EDebugClientState::Connected
        && ClientState!=EDebugClientState::Paused
    )
    {
        //debug stopped, no need to sync
        FileBreakpointStates.Remove(InFileID);
        return;
    }

    auto State=FileBreakpointStates.Find(InFileID);
    if(State&&State->bIsWaitingBreakpointResponse)
    {

        //already sent request, wait for response
        State->bRequireSyncBreakpointsLater=true;
        return;
    }

    CString JsonString=CreateFileBreakpointJson(InFileID);
    SendToServer(U"FileBreakpoint:"+JsonString);

    if(State)
    {
        State->bIsWaitingBreakpointResponse=true;
    }
    else
    {
        SFileBreakpointState NewState;
        NewState.FileID=InFileID;
        NewState.bIsWaitingBreakpointResponse=true;
        FileBreakpointStates.Add(InFileID,NewState);
    }
}

void REditorScriptDebugSubsystem::ConnectToServer(uint16_t Port)
{
    Client=CTCPClient::Create();
    Client->Connect(SIPAddress::LocalHostIPv4,Port);
    if(!Client->IsConnected())
    {
        WH::PrintLine(U"无法连接到项目进程");
        return;
    }
    ClientState= EDebugClientState::Initializing;

    Client->OnDataReceived.Bind(this,&REditorScriptDebugSubsystem::OnDataReceived);
    SendToServer(U"Attach:Editor");

}

void REditorScriptDebugSubsystem::OnDataReceived()
{
    std::lock_guard<std::mutex> Lock(ReceivedDataMutex);
    RecvBuffer.Append(Client->ReadAllData());
}

void REditorScriptDebugSubsystem::SendToServer(const CString &InMessage)
{
    CString8 CommandString=InMessage.ToUtf8String();

    uint32_t CommandSize=CommandString.CStrLength();

    //convert commandsize to 10 base number
    //and fill to 8 number
    CString SizeString(CommandSize);
    assert(SizeString.GetLength()<=8);
    if(SizeString.GetLength()<8)
    {
        SizeString=CString(U'0').Repeat(8-SizeString.GetLength())+SizeString;
    }

    Client->Send(SizeString);
    Client->Send(CommandString);
}


void REditorScriptDebugSubsystem::OnResponseAttach(const CString &InMessage)
{
    if(ClientState==EDebugClientState::Initializing)
    {
        ClientState=EDebugClientState::Connected;
        WH::PrintLine(U"程序初始化完成，开始调试");

        OnDebugStarted.Broadcast();

        SyncAllBreakpoints();
    }
}

void REditorScriptDebugSubsystem::OnResponseContinue(const CString &InMessage)
{
    if(ClientState==EDebugClientState::Paused)
    {
        ClientState=EDebugClientState::Connected;
        WH::PrintLine(U"程序已经继续运行");

        OnResumed();
    }
}

void REditorScriptDebugSubsystem::OnResponseProjectPrint(const CString &InMessage)
{
    assert(InMessage.StartsWith(U"Output:"));

    auto Message=InMessage.SubString(7);

    OnProjectPrint.Broadcast(Message);
}

// void REditorScriptDebugSubsystem::OnResponseDetach(const CString &InMessage)
// {
//     if(ClientState==EDebugClientState::NotRunning)
//     {
//         return;
//     }

//     ClientState=EDebugClientState::NotRunning;
//     WH::PrintLine(U"程序已经断开连接");

//     FileBreakpointStates.Clear();
    
// }

CString REditorScriptDebugSubsystem::CreateFileBreakpointJson(CString FileID)
{
    auto Breakpoints=GetBreakpointLineList(FileID);

    auto Root= std::make_shared<CJsonObjectNode>();

    {
        auto FilePathNode=std::make_shared<CJsonStringNode>(FileID);
        Root->Add(U"FilePath",FilePathNode);
    }
    
    auto BreakpointsNode=std::make_shared<CJsonVectorNode>();
    for(int64_t BreakpointIndex=0;BreakpointIndex<Breakpoints.Num();++BreakpointIndex)
    {
        auto Breakpoint=Breakpoints[BreakpointIndex];
        auto BreakpointNode=std::make_shared<CJsonObjectNode>();

        {
            auto LineNode=std::make_shared<CJsonIntNode>(Breakpoint->LineIndex);
            BreakpointNode->Add(U"Line",LineNode);
        }

        {
            auto bEnabledNode=std::make_shared<CJsonBoolNode>(Breakpoint->bEnabled);
            BreakpointNode->Add(U"bEnabled",bEnabledNode);
        }

        BreakpointsNode->Add(BreakpointNode);
    }

    Root->Add(U"Breakpoints",BreakpointsNode);

    return CJsonUtils::ToString(Root);

}

void REditorScriptDebugSubsystem::PostBreakpointAdded(const CString &FileID, int64_t InLine)
{
    OnBreakpointAdded.Broadcast(FileID,InLine);
    SyncBreakpoints(FileID);
}

void REditorScriptDebugSubsystem::PostBreakpointRemoved(const CString &FileID, int64_t InLine)
{
    OnBreakpointRemoved.Broadcast(FileID,InLine);
    SyncBreakpoints(FileID);
}

void REditorScriptDebugSubsystem::PostBreakpointStateChanged(const CString &FileID, int64_t InLine)
{
    SyncBreakpoints(FileID);
}

void REditorScriptDebugSubsystem::OnResponseFileBreakpoint(const CString &InMessage)
{
    assert(InMessage.StartsWith(U"FileBreakpointResponse:OK!"));

    auto JsonString= InMessage.SubString(26);
    auto Root=CJsonUtils::Parse(JsonString)->ToObject();

    CString FileID = Root->Get(U"FilePath")->ToString()->GetValue();
    
        
    auto BreakpointsJson=Root->Get(U"Breakpoints")->ToList();

    TVector<TTuple<int64_t,bool>> Breakpoints;
    for(int32_t i=0;i<BreakpointsJson->Num();++i)
    {
        auto BreakpointJson=BreakpointsJson->Get(i)->ToObject();

        int64_t Line=BreakpointJson->Get(U"Line")->ToInt()->GetValue();
        bool bEnabled=BreakpointJson->Get(U"bEnabled")->ToBool()->GetValue();

        auto Breakpoint=WH::MakeTuple(Line,bEnabled);

        Breakpoints.Add(Breakpoint);
    }

    //update the breakpoint
    bDonotSyncBreakpoints=true;
    SetBreakpoints(FileID,Breakpoints);
    bDonotSyncBreakpoints=false;

    //update the state
    auto State=FileBreakpointStates.Find(FileID);
    if(State)
    {
        State->bIsWaitingBreakpointResponse=false;
        if(State->bRequireSyncBreakpointsLater)
        {
            State->bRequireSyncBreakpointsLater=false;
            SyncBreakpoints(FileID);
        }
    }

}

void REditorScriptDebugSubsystem::OnResponseHitBreakpoint(const CString &InMessage)
{
    ClientState = EDebugClientState::Paused;    

    WH::PrintLine(U"程序命中断点，已经暂停");

    OnPaused();
}

void REditorScriptDebugSubsystem::OnPaused()
{
    assert(ClientState==EDebugClientState::Paused);

    //1. update callstack
    SendToServer(U"Trace:Editor");

    //2. update local variables
    RequestLocalVariable();
}

void REditorScriptDebugSubsystem::OnResumed()
{
    assert(ClientState==EDebugClientState::Connected);

    //clear callstack
    SetCallStack(TVector<SEditorCallStackFrame>());

    //clear local variables
    SetSelectedFrameData(SEditorScriptFrameData());
}

void REditorScriptDebugSubsystem::OnResponseTrace(const CString &InMessage)
{
    assert(InMessage.StartsWith(U"TraceResponse:OK!"));

    CString JsonString= InMessage.RemoveAt(0,CString(U"TraceResponse:OK!").GetLength());

    auto Json=CJsonUtils::Parse(JsonString);

    assert(Json->IsList());
    auto RootList=Json->ToList();

    TVector<SEditorCallStackFrame> Frames;

    for(auto TraceJson: RootList->GetNodes())
    {
        assert(TraceJson->IsObject());

        auto JsonObject= TraceJson->ToObject();

        SEditorCallStackFrame& Frame= Frames.Emplace();
        Frame.FileName= JsonObject->Get(U"FilePath")->ToString()->GetValue();
        Frame.LineNumber=JsonObject->Get(U"Line")->ToInt()->GetValue();
        Frame.FunctionName=JsonObject->Get(U"FunctionName")->ToString()->GetValue();
    }

    SetCallStack(Frames);

}

void REditorScriptDebugSubsystem::RequestLocalVariable()
{
    auto RequestJson=std::make_shared<CJsonObjectNode>();
    RequestJson->Add(U"FrameIndex",std::make_shared<CJsonIntNode>(
        SelectedFrame==-1? 0 : SelectedFrame
    ));

    SendToServer(U"LocalVariable:Editor"
        + CJsonUtils::ToString(RequestJson)
    );
}

void REditorScriptDebugSubsystem::OnResponseLocalVariables(const CString &InMessage)
{
    if(InMessage.StartsWith(U"LocalVariableResponse:Error!")
        ||InMessage.StartsWith(U"LocalVariableResponse:OK!{}")
    )
    {
        //no variable or meet error
        //clear variables
        //clear local variables
        SetSelectedFrameData(SEditorScriptFrameData());
        return;
    }

    assert(InMessage.StartsWith(U"LocalVariableResponse:OK!"));


    CString JsonString= InMessage.RemoveAt(0,CString(U"LocalVariableResponse:OK!").GetLength());

    auto RootJson=CJsonUtils::Parse(JsonString)->ToObject();

    SEditorScriptFrameData FrameData;

    auto ParametersJson=RootJson->Get(U"Parameters")->ToList();
    for(int64_t i=0;i<ParametersJson->Num();++i)
    {
        auto ParameterJson=ParametersJson->Get(i)->ToObject();

        SEditorScriptValue VariableData;
        VariableData.Name=ParameterJson->Get(U"Name")->ToString()->GetValue();
        ParseLocalVariableValue(ParameterJson->Get(U"Object")->ToObject(),VariableData);
        
        FrameData.Parameters.Add(VariableData);
    }

    auto LocalVariablesJson=RootJson->Get(U"LocalVariables")->ToList();
    for(int64_t i=0;i<LocalVariablesJson->Num();++i)
    {
        auto LocalVariableJson=LocalVariablesJson->Get(i)->ToObject();

        SEditorScriptValue VariableData;
        VariableData.Name=LocalVariableJson->Get(U"Name")->ToString()->GetValue();
        ParseLocalVariableValue(LocalVariableJson->Get(U"Object")->ToObject(),VariableData);

        FrameData.LocalVariables.Add(VariableData);
    }

    SetSelectedFrameData(FrameData);
}

void REditorScriptDebugSubsystem::ParseLocalVariableValue(std::shared_ptr<CJsonObjectNode> InNode, SEditorScriptValue &OutValue)
{
    OutValue.Value=InNode->Get(U"Value")->ToString()->GetValue();    
    auto ReferenceJson=InNode->Get(U"Reference")->ToObject();
    OutValue.Reference.TypeName=ReferenceJson->Get(U"TypeName")->ToString()->GetValue();
    OutValue.Reference.Address=ReferenceJson->Get(U"Address")->ToString()->GetValue();

    OutValue.NamedChildNum=InNode->Get(U"NamedChildrenNum")->ToInt()->GetValue();
    OutValue.IndexedChildNum=InNode->Get(U"IndexedChildrenNum")->ToInt()->GetValue();
}

void REditorScriptDebugSubsystem::OnResponseReferenceChildren(const CString &InMessage)
{
    if(InMessage.StartsWith(U"ReferenceChildrenResponse:Error!"))
    {
        //meet error
        return;
    }

    CString JsonString= InMessage.RemoveAt(0,CString(U"ReferenceChildrenResponse:OK!").GetLength());

    auto RootJson=CJsonUtils::Parse(JsonString)->ToObject();

    auto ReferenceJson=RootJson->Get(U"Reference")->ToObject();

    SEditorScriptValueReference Reference;
    Reference.TypeName=ReferenceJson->Get(U"TypeName")->ToString()->GetValue();
    Reference.Address=ReferenceJson->Get(U"Address")->ToString()->GetValue();

    TVector<SEditorScriptValue> Children;
    auto NamedChildrenJson=RootJson->Get(U"NamedChildren")->ToList();
    for(int64_t i=0;i<NamedChildrenJson->Num();++i)
    {
        auto ChildJson=NamedChildrenJson->Get(i)->ToObject();

        SEditorScriptValue Child;
        Child.Name=ChildJson->Get(U"Name")->ToString()->GetValue();
        ParseLocalVariableValue(ChildJson->Get(U"Object")->ToObject(),Child);

        Children.Add(Child);
    }

    auto IndexedChildrenJson=RootJson->Get(U"IndexedChildren")->ToList();
    for(int64_t i=0;i<IndexedChildrenJson->Num();++i)
    {
        auto ChildJson=IndexedChildrenJson->Get(i)->ToObject();

        SEditorScriptValue Child;
        int64_t Index=ChildJson->Get(U"Index")->ToInt()->GetValue();
        Child.Name=U"["+ CString(Index)+ U"]";
        ParseLocalVariableValue(ChildJson->Get(U"Object")->ToObject(),Child);

        Children.Add(Child);
    }

    OnReferenceChildrenReceived.Broadcast(Reference,Children);

}

// void REditorScriptDebugSubsystem::OnProjectExited(int64_t, bool)
// {
//     PostProjectExited();
// }

void REditorScriptDebugSubsystem::PostProjectExited()
{
    Client= nullptr;
    ClientState=EDebugClientState::NotRunning;
    WH::PrintLine(U"程序已经断开连接");

    FileBreakpointStates.Clear();

    //clear callstack
    SetCallStack(TVector<SEditorCallStackFrame>());
  
    //clear local variables
    SetSelectedFrameData(SEditorScriptFrameData());

    OnDebugStopped.Broadcast();
}

void REditorScriptDebugSubsystem::OnProjectError()
{
    TVector<uint8_t> StringData = ProjectProcess->ReadAllStandardError();
    StringData.Add(u8'\0');
    CString String((char8_t*)StringData.Data());
  
    //控制台输出
    OnProjectPrint.Broadcast(String);
    
}

void REditorScriptDebugSubsystem::Tick(float DeltaTime)
{
    //在这里处理收到的消息
    if(!Client)
    {
        return;
    }

    if(!Client->IsConnected())
    {
        //连接已断开
        PostProjectExited();
        return;
    }

    std::lock_guard<std::mutex> Lock(ReceivedDataMutex);
    while(RecvBuffer.Num()>8)
    {
        uint32_t MessageSize=0;
        for(int i=0;i<8;++i)
        {
            MessageSize*=10;
            MessageSize+=RecvBuffer[i]-U'0';
        }

        if(RecvBuffer.Num()<(8+MessageSize))
        {
            return;
        }

        //parse message
        TVector<uint8_t> MessageData;
        MessageData.Resize(MessageSize);

        memcpy(MessageData.Data(),RecvBuffer.Data()+8,MessageSize);
        RecvBuffer.Remove((uint64_t)0,(uint64_t)(8+MessageSize),false);

        CString MessageString=CTCPClient::ConvertDataToString(MessageData);

        if(MessageString.StartsWith(U"AttachResponse"))
        {
            OnResponseAttach(MessageString);
            continue;
        }
        
        // if(MessageString.StartsWith(U"DetachResponse"))
        // {
        //     OnResponseDetach(MessageString);
        //     continue;
        // }

        if(MessageString.StartsWith(U"FileBreakpointResponse"))
        {
            OnResponseFileBreakpoint(MessageString);
            continue;
        }

        if(MessageString.StartsWith(U"ToClient:HitBreakpoint!"))
        {
            OnResponseHitBreakpoint(MessageString);
            continue;
        }

        if(MessageString.StartsWith(U"ContinueResponse:"))
        {
            OnResponseContinue(MessageString);
            continue;
        }
        
        if(MessageString.StartsWith(U"TraceResponse:"))
        {
            OnResponseTrace(MessageString);
            continue;
        }

        if(MessageString.StartsWith(U"LocalVariableResponse:"))
        {
            OnResponseLocalVariables(MessageString);
            continue;
        }

        if(MessageString.StartsWith(U"Output:"))
        {
            OnResponseProjectPrint(MessageString);
            continue;
        }

        if(MessageString.StartsWith(U"ReferenceChildrenResponse:"))
        {
            OnResponseReferenceChildren(MessageString);
            continue;
        }

        // assert(false && "unhandled message");

    }    
}
